/* hc256.c
**
** Copyright (c) 2007, Meadhbh S. Hamrick
** All rights reserved.
** 
** Redistribution  and  use  in  source  and  binary  forms,  with  or  without
** modification, are permitted provided that the following conditions are met:
** 
**  * Redistributions of  source code must  retain the above  copyright notice,
**    this list of conditions and the following disclaimer.
** 
**  * Redistributions in binary form must reproduce the above copyright notice,
**    this list of conditions and the following disclaimer in the documentation
**    and/or other materials provided with the distribution.
** 
**  * Neither the  name of  Project Meadhbh  nor the names of  its contributors
**    may be used  to endorse  or promote  products derived from this  software
**    without specific prior written permission.
** 
** THIS SOFTWARE IS PROVIDED BY  THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
** AND ANY  EXPRESS OR IMPLIED WARRANTIES,  INCLUDING, BUT NOT  LIMITED TO, THE
** IMPLIED WARRANTIES  OF MERCHANTABILITY AND FITNESS FOR  A PARTICULAR PURPOSE
** ARE DISCLAIMED.  IN NO  EVENT SHALL THE  COPYRIGHT OWNER OR  CONTRIBUTORS BE
** LIABLE  FOR  ANY  DIRECT,   INDIRECT,  INCIDENTAL,  SPECIAL,  EXEMPLARY,  OR
** CONSEQUENTIAL  DAMAGES  (INCLUDING,  BUT  NOT  LIMITED  TO,  PROCUREMENT  OF
** SUBSTITUTE GOODS  OR SERVICES;  LOSS OF USE,  DATA, OR PROFITS;  OR BUSINESS
** INTERRUPTION)  HOWEVER CAUSED  AND ON  ANY THEORY  OF LIABILITY,  WHETHER IN
** CONTRACT,  STRICT LIABILITY,  OR  TORT (INCLUDING  NEGLIGENCE OR  OTHERWISE)
** ARISING IN ANY WAY  OUT OF THE USE OF THIS SOFTWARE,  EVEN IF ADVISED OF THE
** POSSIBILITY OF SUCH DAMAGE.
** 
** $Id: hc256.c 66 2007-11-08 07:18:51Z msh.mobile $
*/

/** \file hc256.c
 ** This file implements the HC256 ("Alleged" RC-4) stream cipher
 */

/* Macro Definitions */

/* File Includes */
#include "mutil.h"
#include "mcrypto/hc256.h"

/* Static Prototypes */

/* Static Variables */

/** \fn void mc_stream_hc256_clone( tMCHc256 *new, tMCHc256 *old )
**  \brief copy the state of an HC256 context
*/
void mc_stream_hc256_clone( tMCHc256 *new_context, tMCHc256 *old_context ) {
  memcpy( new_context, old_context, sizeof( tMCHc256 ) );
}

/** \fn void mc_stream_hc256_clear( tMCHc256 *context )
**  \brief clear sensitive data from the context
*/
void mc_stream_hc256_clear( tMCHc256 *context ) {
  memset( context, 0, sizeof( tMCHc256 ) );
}

/** \fn void mc_stream_hc256_initialize( tMCHc256 *context, uint8_t *key, uint32_t key_length )
**  \brief initialize a HC256 context with the given key
*/
void mc_stream_hc256_initialize( tMCHc256 *context, uint8_t *key, uint32_t key_length ) {
  uint32_t i,j,t;

  context->a = 0;
  context->b = 0;

  for( i = 0; i < 256; i++ ) {
    context->sbox[i] = i;
  }

  j = 0;
  for( i = 0; i < 256; i++ ) {
    j = ( j + (uint32_t) context->sbox[i] + (uint32_t) key[ i % key_length ] ) & 0xFF;
    t = (uint32_t) context->sbox[ i ];
    context->sbox[ i ] = context->sbox[ j ];
    context->sbox[ j ] = (uint8_t) t;
  }
}
 
/** \fn void mc_stream_hc256_update( tMCHc256 *context, uint8_t *input, uint32_t input_length, uint8_t *output, uint32_t output_length, uint32_t *bytes_written )
**  \brief encrypt plaintext using an initialized context
*/
void mc_stream_hc256_update( tMCHc256 *context, uint8_t *input, uint32_t input_length, uint8_t *output, uint32_t output_length, uint32_t *bytes_written ) {
  uint32_t count, i;

  count = ( input_length > output_length ) ? output_length : input_length;

  mc_stream_hc256_keystream( context, output, count );

  for( i = 0; i < count; i++ ) {
    output[i] ^= input[i];
  }

  if( NULL != bytes_written ) {
    *bytes_written = count;
  }
}

/** \fn void mc_stream_hc256_keystream( tMCHc256 *context, uint8_t *output, uint32_t output_length )
**  \brief produce keystream bytes
*/
void mc_stream_hc256_keystream( tMCHc256 *context, uint8_t *output, uint32_t output_length ) {
  uint32_t a, b, i, k, t;

  if( NULL == output ) {
    k = 0;
  } else {
    k = 1;
  }

  a = context->a;
  b = context->b;

  for( i = 0; i < output_length; i ++ ) {
    a = ( a + 1 ) & 0xFF;
    b = ( b + context->sbox[ a ] ) & 0xFF;
    t = (uint32_t) context->sbox[ a ];
    context->sbox[ a ] = context->sbox[ b ];
    context->sbox[ b ] = (uint8_t) t;
    
    if( 0 != k ) {
      output[ i ] = context->sbox[ ( ( uint32_t ) context->sbox[ a ] + ( uint32_t ) context->sbox[ b ] ) & 0xFF ];
    }
  }

  context->a = a;
  context->b = b;
}
